﻿// 2018/8/24: 首个记录
// 2019/3/29: 添加注释
// 2019/10/11: 修改pragma once和include
// 2020/1/14: 接口完整性
// 2020/11/12: 支持Spadas7
// 2022/11/2: 支持Spadas8.4
// 2023/10/10: 补充部分英文注释
// 2024/7/3: 基于新样本框架
// 2025/3/25: 实现fromSample
// 2025/3/26: 实现getProtocolNames
// 2025/8/4: 不使用cat宏
// 2025/8/4: 修正fromSample未赋值时间戳问题
// 2025/8/4: 改为继承SessionSample。改回单头文件

#ifndef FREESPACE_SAMPLE_V1_H
#define FREESPACE_SAMPLE_V1_H

#include "spadas.h"

namespace freespace_sample_v1
{
	using namespace spadas;

	struct FreespacePointSequence
	{
		Array<Point2D> points; // [m] List of points for each sequence / 每段可行驶区域的顶点列表
	};

	struct FreespaceSampleData
	{
		Array<FreespacePointSequence> sequences; // List of point sequences (Freespace can be described in multiple sequences) / 可行驶区域列表(可多段描述)
	};

	class FreespaceSample : public SessionSample
	{
	private:
		class FreespaceSampleVars : public SessionSampleVars
		{
		public:
			SPADAS_VARS(FreespaceSample, SessionSampleVars)
			FreespaceSampleData data;

			SessionGeneralSample toGeneralSample()
			{
				SessionGeneralSample sample;
				sample.timestamp() = timestamp;

				UInt pointCount = 0;
				for (UInt i = 0; i < data.sequences.size(); i++)
				{
					pointCount += data.sequences[i].points.size();
				}

				Array<GeneralElement> values(1 + 2 * (data.sequences.size() + pointCount));
				sample.values() = values;

				UInt seqBase = 1;
				UInt ptBase = seqBase + 2 * data.sequences.size();

				values[0] = data.sequences.size();

				pointCount = 0;
				for (UInt i = 0; i < data.sequences.size(); i++)
				{
					Array<Point2D> pts = data.sequences[i].points;

					values[seqBase + 2 * i] = pointCount;
					values[seqBase + 2 * i + 1] = pts.size();

					for (UInt n = 0; n < pts.size(); n++)
					{
						values[ptBase + 2 * (pointCount + n)] = pts[n].x;
						values[ptBase + 2 * (pointCount + n) + 1] = pts[n].y;
					}

					pointCount += pts.size();
				}

				sample.significantCount() = values.size();
				return sample;
			}

			Bool fromSample(String protocol, SessionSample sample) override
			{
				SessionGeneralSample generalSample;
				if (!sample.is<SessionGeneralSample>(generalSample)) return FALSE;

				timestamp = generalSample.timestamp();
				auto values = generalSample.values();

				if (protocol.startsWith("freespace-sample-v1"))
				{
					if (values.size() < 1) return FALSE;

					UInt seqCount = (UInt)values[0].value;
					if (seqCount == 0)
					{
						data.sequences = Array<FreespacePointSequence>();
						return TRUE;
					}

					UInt seqBase = 1;
					if (values.size() < seqBase + 2 * seqCount) return FALSE;

					data.sequences = Array<FreespacePointSequence>(seqCount);

					UInt totalPoints = 0;
					for (UInt i = 0; i < seqCount; i++)
					{
						UInt pointCount = (UInt)values[seqBase + 2 * i + 1].value;
						totalPoints += pointCount;
						data.sequences[i].points = Array<Point2D>(pointCount);
					}

					UInt ptBase = seqBase + 2 * seqCount;
					if (values.size() < ptBase + 2 * totalPoints) return FALSE;

					UInt pointIndex = 0;
					for (UInt i = 0; i < seqCount; i++)
					{
						Array<Point2D>& points = data.sequences[i].points;
						for (UInt n = 0; n < points.size(); n++)
						{
							points[n].x = values[ptBase + 2 * pointIndex].value;
							points[n].y = values[ptBase + 2 * pointIndex + 1].value;
							pointIndex++;
						}
					}

					return TRUE;
				}
				return FALSE;
			}
		};

	public:
		SPADAS_CLASS("freespace_sample_v1.FreespaceSample", FreespaceSampleVars)

		FreespaceSample() : SessionSample(new FreespaceSampleVars)
		{}
		FreespaceSampleData *operator ->()
		{
			return &var()->data;
		}

		static String getProtocolName(UInt channel)
		{
			return (String)"freespace-sample-v1@" + channel;
		}
		SessionGeneralSample toGeneralSample()
		{
			return var()->toGeneralSample();
		}

		static Array<String> getProtocolNames(UInt channel)
		{
			return Array<String>::create(1, "freespace-sample-v1").convert<String>([channel](auto& s){ return s + "@" + channel; });
		}
		Bool fromGeneralSample(String protocol, SessionGeneralSample generalSample)
		{
			return fromSample(protocol, generalSample);
		}
	};
}

#endif